﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace ZoambieGaem {
    class Logic {



        public void update(Master m) {

            if (m.gameJustStarted) {
                createConvexHulls(m);
                m.gameJustStarted = false;
            }
            if (m != null && m.player != null) {
                updateShadow(m);
                updateDamageMovement(m);
                createRouteTable(m);
                updateWeapon(m);
                updateBlood(m);
                playerCollisionDetect(m);
                updateScreenPosition(m);
            }
        }




        private void updateBlood(Master m) {
            for (int i = m.bloodList.Count - 1 ; i >= 0; i -- ) {
                try {
                    Blood b = m.bloodList.ElementAt(i);
                    b.update(m);
                }
                catch (Exception e) { Console.WriteLine("BLOOD UPDATE FAILURE " + e); };
            }
        }



        private void updateDamageMovement(Master m) {
            if (m.player != null && m.player.gettingDamage) {

                Vector2 nextPos = new Vector2(m.player.worldPosition.X + (float)(Math.Cos(m.player.damageAngle) * m.player.damageSpeed),
                                          m.player.worldPosition.Y + (float)(Math.Sin(m.player.damageAngle) * m.player.damageSpeed));
                m.player.worldPosition = nextPos;

                m.player.damageSpeed -= 0.75f;
                if (m.player.damageSpeed < 0.2)
                    m.player.gettingDamage = false;
            }
        }



        private void updateWeapon(Master m) {

            if (m.wpn.fireRateCounter > m.wpn.fireRate)
                m.wpn.fireRateCounter = m.wpn.fireRate;
            else
                m.wpn.fireRateCounter++;
           
            if (m.wpn.reloadCounter > m.wpn.reloadTime)
                m.wpn.reloadCounter = m.wpn.reloadTime;
            else
                m.wpn.reloadCounter++;
        }





        private void playerCollisionDetect(Master m) {
            int zx = (int)(m.player.worldPosition.X / m.blockSize);
            int zy = (int)(m.player.worldPosition.Y / m.blockSize);
            float zyy = ((m.player.worldPosition.Y / (float)m.blockSize)) - zy;
            float zxx = ((m.player.worldPosition.X / (float)m.blockSize)) - zx;
            try {
                if (zx >= 0 && zy >= 0 && zx < Maps.width && zy < Maps.height) {
                    if (zxx < 0.4) {
                        if (zx - 1 >= 0) {
                            if (Maps.map[zy][zx - 1] == 1) {
                                m.player.worldPosition = new Vector2(m.player.worldPosition.X + 3, m.player.worldPosition.Y);
                            }
                        }
                    }
                    else if (zxx > 0.6) {
                        if (zx + 1 < Maps.width) {
                            if (Maps.map[zy][zx + 1] == 1) {
                                m.player.worldPosition = new Vector2(m.player.worldPosition.X - 3, m.player.worldPosition.Y);
                            }
                        }
                    }
                    if (zyy < 0.4) {
                        if (zy - 1 >= 0) {
                            if (Maps.map[zy - 1][zx] == 1) {
                                m.player.worldPosition = new Vector2(m.player.worldPosition.X, m.player.worldPosition.Y + 3);
                            }
                        }
                    }
                    else if (zyy > 0.6) {
                        if (zy + 1 < Maps.height) {
                            if (Maps.map[zy + 1][zx] == 1) {
                                m.player.worldPosition = new Vector2(m.player.worldPosition.X, m.player.worldPosition.Y - 3);
                            }
                        }
                    }
                }
            }
            catch (Exception e) {
                Console.WriteLine("PLAYER COLLISION DETECT FAILURE " + e);
            }
        }








        //Creates a routemap for zombies, where lowest value means fastest route
        private void createRouteTable(Master m) {
            if (m.player != null) {
                m.player.routeUpdate++;
                if (m.player.routeUpdate > 60) {

                    int deadCell = 9999999;

                    // Creates a new empty routemap with extremely high values
                    int[][] routeMap = new int[Maps.height][];
                    for (int y = 0; y < Maps.height; y++) {
                        int[] row = new int[Maps.width];
                        for (int x = 0; x < Maps.width; x++) {
                            row[x] = deadCell;
                        }
                        routeMap[y] = row;
                    }

                    int playerX = (int)(m.player.worldPosition.X / m.blockSize);
                    int playerY = (int)(m.player.worldPosition.Y / m.blockSize);

                    Node n = new Node(playerX, playerY);
                    n.g = 0;

                    //Adds player location to list
                    //LinkedList<Node> nodesLeft = new LinkedList<Node>();
                    BinaryHeap nodesLeft = new BinaryHeap();
                    BinaryHeap oldNodes = new BinaryHeap();
                    //nodesLeft.AddFirst(n);
                    nodesLeft.Add(n);

                    //Loop the list while it still has nodes to check
                    while (!nodesLeft.empty()) {
                        Node current = nodesLeft.First();
                        oldNodes.Add(current);
                        //nodesLeft.RemoveFirst();
                        for (int i = -1; i < 2; i++) {
                            for (int j = -1; j < 2; j++) {
                                //OutOfBounds check
                                if (current.x + j >= 0 &&
                                    current.x + j < Maps.width &&
                                    current.y + i >= 0 &&
                                    current.y + i < Maps.height) {

                                    //If the cell has been visited -> skip
                                    if (routeMap[current.y + i][current.x + j] == deadCell) {
                                        //If the node is wall -> skip
                                        if (Maps.map[current.y + i][current.x + j] != 1) {


                                            Node newNode = new Node(current.x + j, current.y + i);
                                            if (i == 0 || j == 0) {
                                                newNode.g = current.g + 10;
                                            }
                                            else
                                                newNode.g = current.g + 14;


                                            //Update routemap and add the newNode to nodesLeft list
                                            routeMap[current.y + i][current.x + j] = newNode.g;
                                            //nodesLeft.AddLast(newNode);
                                            nodesLeft.Add(newNode);

                                        }
                                    }

                                }
                            }
                        }
                    }

                    m.routeMap = routeMap;
                    m.player.routeUpdate = 0;
                }

            }
        }



        //Movement update for the 'camera' - next coordinates for center screen
        private void updateScreenPosition(Master m) {

            Vector2 direction = new Vector2(0, 0);
            direction.X = ((m.mouse.X + m.player.screenPosition.X) / 2) - (m.screenWidth / 2);
            direction.Y = ((m.mouse.Y + m.player.screenPosition.Y) / 2) - (m.screenHeight / 2);

            double vx = m.mouseFollower.X - m.mouse.X;
            double vy = m.mouseFollower.Y - m.mouse.Y;

            double speed = Math.Sqrt(vx * vx + vy * vy) / 10;
            double angle = (float)(Math.Atan2(direction.Y, direction.X)) + (float)Math.PI;

            m.mouseFollower = new Vector2(
                m.mouseFollower.X + (float)(Math.Cos((double)angle) * -speed),
                m.mouseFollower.Y + (float)(Math.Sin((double)angle) * -speed));

        }


        




        private void createConvexHulls(Master m) {
            LinkedList<Point[]> points = Maps.createHull(m);

            /*
            LinkedList<Point[]> points = new LinkedList<Point[]>();
            for (int y = 0; y < Maps.height; y++) {
                for (int x = 0; x < Maps.width; x++) {
                    if (Maps.map[y][x] == 1) {
                        Point[] p = {
                                        
                            new Point(x * m.blockSize, y * m.blockSize + m.blockSize),
                            
                            new Point(x * m.blockSize + m.blockSize, y * m.blockSize + m.blockSize),
                            
                            new Point(x * m.blockSize + m.blockSize, y * m.blockSize),

                            new Point(x * m.blockSize, y * m.blockSize),
                        };

                        points.AddFirst(p);
                    }
                }
            } */
            m.convexHulls = points;
        }











        private void updateShadow(Master m) {
            if (m.drawShadows) {
                LinkedList<Vector2[]> shadows = new LinkedList<Vector2[]>();
                if (m.convexHulls != null) {
                    foreach (Point[] p in m.convexHulls) {
                        for (int i = 0; i < p.Count(); i++) {
                            int j = i + 1;
                            if (j == p.Count())
                                j = 0;

                            Vector2 shadow1 = shadowCalculating(new Vector2((float)p.ElementAt(i).x, (float)p.ElementAt(i).y), m);
                            Vector2 shadow2 = shadowCalculating(new Vector2((float)p.ElementAt(j).x, (float)p.ElementAt(j).y), m);

                            Vector2[] vects = {
                        new Vector2((float)p.ElementAt(i).x, (float)p.ElementAt(i).y),
                        new Vector2((float)p.ElementAt(j).x, (float)p.ElementAt(j).y),
                        shadow1,
                    };
                            shadows.AddFirst(vects);

                            Vector2[] vects2 = {
                        new Vector2((float)p.ElementAt(j).x, (float)p.ElementAt(j).y),
                        shadow2,
                        shadow1,
                    };
                            shadows.AddFirst(vects2);
                        }
                    }
                    m.shadows = shadows;
                }
            }
        }




        private Vector2 shadowCalculating(Vector2 arg1, Master m) {
            Vector2 direction = new Vector2(
                   m.player.worldPosition.X - arg1.X,
                   m.player.worldPosition.Y - arg1.Y);

            double dir = (float)(Math.Atan2(direction.Y, direction.X));

            Vector2 result = new Vector2(
                    ((int)(arg1.X + (Math.Cos(dir) * -m.shadowLength))),
                    ((int)(arg1.Y + (Math.Sin(dir) * -m.shadowLength)))
            );

            return result;
        }
    }
}